{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import scipy.io as sio\n",
    "import scipy.sparse as ss\n",
    "import _pickle as cPickle\n",
    "import os\n",
    "import scipy.spatial.distance as ssd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 用户和item索引\n",
    "users_index = cPickle.load(open('ml-100k/users_index.pkl','rb'))\n",
    "items_index = cPickle.load(open('ml-100k/items_index.pkl','rb'))\n",
    "\n",
    "n_users = len(users_index)\n",
    "n_items = len(items_index)\n",
    "\n",
    "# 到排序\n",
    "#每个用户打过分的电影\n",
    "user_items = cPickle.load(open('ml-100k/user_items.pkl','rb'))\n",
    "item_users = cPickle.load(open('ml-100k/item_users.pkl','rb'))\n",
    "\n",
    "# 用户关系矩阵\n",
    "user_item_scores = sio.mmread('ml-100k/user_item_scores')\n",
    "user_item_scores = user_item_scores.tocsr()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 计算每个用户的平均打分\n",
    "user_ms = np.zeros(n_users)\n",
    "\n",
    "for i in range(n_users):\n",
    "    score = 0;\n",
    "    n_item = 0;\n",
    "    for item in user_items[i]:\n",
    "        score += user_item_scores[i,item]\n",
    "        n_item += 1\n",
    "    if n_item > 0:\n",
    "        user_ms[i] = score/n_item"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[3.68148148 3.8        3.         4.35714286 2.95604396 3.58181818\n",
      " 3.89201878 3.6        4.16666667 4.21276596 3.53333333 4.28\n",
      " 3.13672922 4.2195122  3.03333333 4.34782609 3.15789474 3.93710692\n",
      " 3.6        3.30769231 2.66315789 3.3        3.63636364 4.3902439\n",
      " 4.04878049 2.90909091 3.3        3.64102564 3.94117647 3.8\n",
      " 3.91304348 3.54545455 3.64285714 3.8        3.         4.\n",
      " 3.45945946 3.9047619  3.84615385 2.72727273 3.93548387 3.625\n",
      " 3.67857143 3.6375     3.48275862 4.18181818 3.5625     3.72727273\n",
      " 2.72897196 3.53846154 3.75       4.34285714 4.         3.33333333\n",
      " 3.8        3.65217391 3.62962963 3.875      4.02325581 4.13445378\n",
      " 2.83333333 3.31707317 2.97916667 3.59633028 3.97916667 3.52380952\n",
      " 3.41666667 3.16666667 3.73684211 3.4025974  3.81818182 3.76712329\n",
      " 3.64705882 3.76190476 3.18181818 3.4        3.46153846 3.35714286\n",
      " 4.18518519 3.92857143 3.56666667 3.12790698 3.3974359  3.74418605\n",
      " 3.54716981 3.63636364 3.77876106 4.09090909 4.15       4.25\n",
      " 3.9        3.23039216 3.         3.66063348 3.48407643 4.25\n",
      " 3.97142857 3.875      3.65079365 3.02941176 3.         2.5625\n",
      " 3.4        2.79365079 3.3125     3.71428571 2.76923077 3.41176471\n",
      " 3.5        3.08536585 3.5        3.91666667 3.80645161 3.62962963\n",
      " 4.04081633 3.05681818 3.72222222 4.64285714 3.87755102 3.57142857\n",
      " 3.67391304 3.97058824 3.93103448 3.5        3.44444444 3.69565217\n",
      " 4.18181818 3.57843137 2.86666667 4.07865169 4.06666667 3.70588235\n",
      " 3.27777778 3.58333333 3.45       4.55       4.08333333 4.2962963\n",
      " 3.93333333 3.6        3.44615385 3.8        3.73333333 3.66666667\n",
      " 3.31891892 3.68421053 4.3        4.         3.         3.8\n",
      " 4.08433735 4.28571429 3.07692308 3.96296296 2.63636364 3.77777778\n",
      " 3.7037037  3.83838384 3.4057971  4.09090909 2.52941176 3.35\n",
      " 3.07692308 4.06060606 4.         3.3        3.81578947 3.25714286\n",
      " 3.95652174 3.66666667 3.42105263 3.         4.18518519 3.80208333\n",
      " 3.82608696 3.64285714 3.73015873 3.71895425 2.91304348 3.85294118\n",
      " 1.51834862 4.14285714 3.16666667 3.71428571 3.875      3.33333333\n",
      " 4.20588235 3.89655172 3.96330275 3.38888889 4.         3.45\n",
      " 3.27868852 2.94444444 3.2244898  3.23809524 3.19230769 3.25471698\n",
      " 3.15789474 4.         3.14418605 2.84615385 3.27272727 3.63157895\n",
      " 2.8        1.87878788 3.21804511 3.72727273 3.375      3.98571429\n",
      " 3.38095238 4.23529412 4.23880597 3.87301587 3.86       3.93150685\n",
      " 3.05405405 3.60606061 4.125      3.8        3.61445783 3.03286385\n",
      " 3.19230769 3.09375    4.8        3.64516129 3.27777778 2.85714286\n",
      " 3.13333333 3.92       3.6        3.89361702 4.35294118 3.10431655\n",
      " 3.89361702 3.35714286 4.11111111 3.21428571 4.11627907 3.6\n",
      " 3.33333333 4.27272727 3.55555556 3.70940171 3.30769231 2.95495495\n",
      " 3.8        3.19047619 4.19101124 3.83076923 3.72727273 4.54545455\n",
      " 3.94642857 3.15294118 2.47826087 4.06956522 4.25806452 3.625\n",
      " 3.81481481 4.16666667 4.1        3.20731707 4.05       4.19047619\n",
      " 3.62962963 3.1875     3.99056604 2.93296089 2.8502994  4.29166667\n",
      " 3.59006211 4.38709677 3.5        4.02564103 3.0625     3.41608392\n",
      " 3.28571429 4.16666667 3.17355372 3.65306122 3.29411765 3.4\n",
      " 4.07142857 3.60714286 3.8125     3.83544304 3.85714286 3.72093023\n",
      " 2.61538462 3.41772152 3.69364162 4.14285714 3.09767442 3.5\n",
      " 4.29       4.25675676 3.40860215 4.19230769 3.50649351 3.75\n",
      " 3.58940397 2.5        3.36594203 3.46153846 3.424      3.33333333\n",
      " 3.66666667 3.83486239 3.81818182 3.         3.75675676 4.48461538\n",
      " 3.64705882 3.67142857 3.97777778 3.11363636 3.58333333 3.6875\n",
      " 3.25       3.73417722 3.81944444 4.22222222 3.63636364 4.39215686\n",
      " 3.35526316 3.22123894 3.28       3.40828402 3.2972973  4.44705882\n",
      " 3.65       4.13793103 3.78571429 3.53211009 3.61538462 3.01176471\n",
      " 4.04761905 4.12195122 4.06329114 4.11111111 3.66666667 3.4765625\n",
      " 3.98726115 3.69724771 3.6641791  3.32835821 3.60330579 3.77419355\n",
      " 3.35714286 4.4        4.13333333 3.79310345 3.28571429 3.71710526\n",
      " 4.1        3.46666667 4.05769231 3.71428571 4.14285714 3.97101449\n",
      " 3.75641026 3.27777778 3.0861244  3.35714286 3.36842105 4.35294118\n",
      " 4.11428571 3.3125     4.         3.67857143 4.1025641  4.22727273\n",
      " 3.71794872 3.31958763 3.9375     3.63636364 4.         3.40530303\n",
      " 4.01470588 3.1637931  3.79775281 3.54545455 4.42307692 4.1875\n",
      " 3.30693069 3.83333333 3.33333333 4.11904762 3.70792079 3.75\n",
      " 3.75789474 4.07407407 3.33968254 3.808      3.82608696 3.30769231\n",
      " 3.8375     3.62307692 2.92       3.64705882 3.11504425 3.83333333\n",
      " 3.63829787 3.25       1.84879725 3.47445255 3.4787234  3.7826087\n",
      " 3.70322581 3.08695652 3.74       3.94594595 3.675      3.95454545\n",
      " 4.28571429 3.85131894 3.23432343 2.86666667 4.17241379 4.21875\n",
      " 3.81818182 3.4125     3.52941176 3.3        2.94382022 3.81609195\n",
      " 4.58333333 3.9375     3.37087912 3.50819672 3.27777778 3.66666667\n",
      " 3.45945946 3.73170732 3.30835735 3.74603175 3.57142857 4.06896552\n",
      " 3.71428571 4.14583333 3.5        3.14516129 3.375      3.71428571\n",
      " 2.00819672 2.74285714 3.57480315 3.37142857 3.74626866 3.8582996\n",
      " 2.73626374 3.43877551 3.27516779 2.97333333 3.5        3.46948357\n",
      " 4.0260223  3.68333333 3.38461538 3.41791045 3.         3.96875\n",
      " 2.86466165 4.03773585 3.4375     3.46534653 3.68181818 3.99300699\n",
      " 4.53488372 3.57894737 3.38709677 4.22813688 3.91428571 4.08256881\n",
      " 3.6        3.37349398 4.45714286 3.4159292  3.42079208 3.73333333\n",
      " 3.98214286 3.42307692 3.05084746 3.99280576 3.03846154 3.22222222\n",
      " 3.64502165 3.35714286 3.72477064 2.78947368 3.84848485 3.52727273\n",
      " 3.75555556 3.87234043 3.95305164 3.03100775 3.17921147 3.2885906\n",
      " 3.8627451  3.34222222 3.76712329 3.10810811 4.01875    3.62151394\n",
      " 3.36607143 3.67355372 4.72413793 3.82758621 2.51515152 2.76666667\n",
      " 3.75       4.33333333 4.36363636 3.84020619 3.         4.0952381\n",
      " 3.2972973  3.64383562 4.12244898 3.43478261 3.14       4.33333333\n",
      " 4.36       3.49346405 3.2962963  3.23529412 3.83211679 3.71698113\n",
      " 3.60465116 3.88888889 3.23333333 4.12043796 3.38461538 4.075\n",
      " 3.93577982 3.87116564 2.86530612 3.61728395 3.98214286 3.71428571\n",
      " 3.62406015 3.48888889 3.53299492 2.80645161 3.50617284 3.89830508\n",
      " 3.65217391 3.67096774 3.72       3.675      3.80239521 3.0952381\n",
      " 4.17       3.57017544 4.01923077 4.20454545 3.75471698 4.2\n",
      " 3.57894737 3.3960396  3.02521008 3.54166667 3.76666667 3.41176471\n",
      " 4.54285714 3.44295302 3.95483871 3.37037037 3.48571429 2.68181818\n",
      " 3.45       3.25       3.74509804 3.52173913 3.15384615 3.52777778\n",
      " 3.80319149 2.66666667 3.47297297 3.55319149 4.03571429 3.27419355\n",
      " 4.44444444 3.54166667 3.7375     3.3253012  2.96938776 3.74222222\n",
      " 3.44186047 3.5        3.6547619  3.81388889 3.60897436 3.48\n",
      " 3.19791667 3.6        3.63414634 3.68       3.89361702 3.58426966\n",
      " 3.08108108 3.79310345 3.78723404 3.2962963  3.75555556 3.96610169\n",
      " 3.83783784 3.67484663 2.39285714 3.73333333 3.83333333 3.37037037\n",
      " 4.32142857 3.12820513 3.86407767 3.62790698 2.62037037 3.28703704\n",
      " 3.50561798 3.82727273 3.5497076  3.39035088 3.73333333 3.53191489\n",
      " 3.43396226 2.34375    3.29281768 4.7037037  4.08264463 3.32407407\n",
      " 3.1        3.66101695 3.31034483 3.37037037 3.26470588 4.45\n",
      " 2.49038462 3.53731343 2.95945946 4.2293578  4.125      3.62264151\n",
      " 3.73786408 4.         3.95901639 2.94736842 3.82758621 3.25925926\n",
      " 3.79166667 3.15434084 3.28571429 3.13636364 2.69257951 3.68707483\n",
      " 2.9080292  2.54166667 3.24137931 3.73239437 3.79057592 2.58482143\n",
      " 3.91735537 3.86956522 3.53164557 3.74698795 3.47887324 3.66530612\n",
      " 3.95744681 3.52173913 3.38       3.84782609 3.58064516 3.24137931\n",
      " 3.88571429 3.65853659 3.70588235 3.58441558 3.59183673 3.42857143\n",
      " 3.66129032 4.         3.22727273 3.13784461 3.22666667 3.48837209\n",
      " 2.05       4.56338028 3.47619048 4.83333333 3.83333333 3.14782609\n",
      " 4.21875    3.275      3.13636364 4.15286624 3.28947368 3.93103448\n",
      " 3.71428571 2.84251969 3.14765101 3.66666667 4.21212121 2.4516129\n",
      " 3.53191489 3.77777778 3.71052632 3.31034483 3.49576271 3.2\n",
      " 3.57246377 3.69767442 3.85972851 3.7654321  2.96666667 3.52272727\n",
      " 3.38323353 3.88847584 3.70967742 3.69230769 3.10447761 3.96666667\n",
      " 3.53571429 3.61904762 3.27272727 2.16470588 3.8        3.\n",
      " 3.03726708 3.57692308 2.85714286 3.23684211 3.38554217 3.7\n",
      " 3.02803738 3.609375   3.32075472 3.         3.96969697 3.67567568\n",
      " 3.6        3.4        3.46153846 3.46153846 3.68571429 3.85185185\n",
      " 3.1        3.64179104 4.05460751 3.72222222 3.62295082 3.09090909\n",
      " 3.54166667 3.20547945 3.59649123 3.38235294 3.10526316 3.19266055\n",
      " 3.36746988 3.8767507  4.0625     3.24390244 2.84848485 3.04761905\n",
      " 3.78294574 3.68807339 4.08695652 3.35428571 4.43243243 3.265625\n",
      " 3.06896552 4.296875   3.71428571 4.         3.27950311 2.05803571\n",
      " 3.78571429 3.46666667 4.05555556 2.95384615 3.91891892 3.87272727\n",
      " 3.78571429 2.7887931  3.86666667 3.84615385 3.7037037  3.87179487\n",
      " 3.14035088 3.34136546 3.66666667 3.01731602 3.8        3.12765957\n",
      " 3.50909091 4.30769231 3.13207547 3.60055866 2.65384615 3.32635983\n",
      " 4.         3.75       4.03846154 3.49333333 3.09090909 3.66566265\n",
      " 3.34798535 3.64285714 3.89215686 4.13043478 3.45       4.42307692\n",
      " 4.14285714 3.5        2.79310345 2.97142857 3.7431694  3.84\n",
      " 3.36111111 3.47619048 4.07142857 3.         4.33870968 3.12\n",
      " 4.00540541 2.6        3.6137931  3.82608696 3.25       3.11538462\n",
      " 3.546875   3.74528302 3.52054795 2.96       3.05617978 3.92592593\n",
      " 4.14705882 3.86       3.04347826 4.11827957 3.19298246 4.13705584\n",
      " 3.90322581 3.74074074 2.73529412 3.50617284 3.12903226 3.74074074\n",
      " 3.03424658 4.28666667 4.86956522 4.52941176 3.45933014 3.46938776\n",
      " 2.97560976 3.21198157 3.34782609 3.42307692 3.27272727 3.42857143\n",
      " 3.84615385 3.23943662 4.         4.36363636 3.12149533 3.82312925\n",
      " 2.28787879 2.7        4.26315789 2.95192308 2.87234043 3.4535316\n",
      " 3.49565217 3.38028169 2.9        3.82352941 4.19101124 4.19047619\n",
      " 3.82716049 3.26865672 3.68965517 3.42663043 3.32046332 4.05109489\n",
      " 3.92537313 3.72093023 3.31372549 3.3625     3.92397661 4.3\n",
      " 3.43558282 3.85714286 4.12765957 4.         3.42372881 3.63673469\n",
      " 3.75       2.98066298 3.96216216 3.5        3.52592593 2.55555556\n",
      " 3.85483871 3.44897959 3.85294118 3.74468085 3.275      3.80487805\n",
      " 4.57142857 3.55405405 4.34615385 3.16666667 3.80612245 3.81132075\n",
      " 3.52272727 3.08695652 3.11538462 3.3659306  3.54285714 3.34951456\n",
      " 3.47004608 3.23076923 3.27272727 3.37007874 4.14864865 3.75609756\n",
      " 3.125      3.3        3.69166667 4.6875     3.69387755 2.96825397\n",
      " 3.72131148 3.96680498 2.64673913 3.70114943 3.92307692 3.74647887\n",
      " 3.375      3.26851852 4.26530612 3.45794393 4.04545455 4.26582278\n",
      " 3.41071429]\n"
     ]
    }
   ],
   "source": [
    "print(user_ms)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def user_similarity(uid1,uid2):\n",
    "    si = {}\n",
    "    for item in user_items[uid1]: # uid1 评分过的item\n",
    "        if item in user_items[uid2]:  # 如果uid2 也打过分\n",
    "            si[item] = 1\n",
    "    n = len(si)\n",
    "    if n == 0:\n",
    "        similarity = 0.0\n",
    "        return similarity\n",
    "    # 用户uid1有效打分\n",
    "    s1 = np.array([user_item_scores[uid1,item]-user_ms[uid1] for item in si])\n",
    "    # 用户uid2有效打分\n",
    "    s2 = np.array([user_item_scores[uid2,item]-user_ms[uid2] for item in si])\n",
    "    \n",
    "    # pearson相关系数的计算正好是 1- ssd.cosine\n",
    "    similarity = 1 - ssd.cosine(s1,s2)\n",
    "    if np.isnan(similarity):\n",
    "        similarity = 0.0\n",
    "    return similarity"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.6507913734559687\n"
     ]
    }
   ],
   "source": [
    "uid1 = 1\n",
    "uid2 = 34\n",
    "print(user_similarity(uid1,uid2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ui = 0\n",
      "ui = 100\n",
      "ui = 200\n",
      "ui = 300\n",
      "ui = 400\n",
      "ui = 500\n",
      "ui = 600\n",
      "ui = 700\n",
      "ui = 800\n",
      "ui = 900\n"
     ]
    }
   ],
   "source": [
    "# 预算好所有用户之间的相似度，可以加快后边的计算\n",
    "# 对于用户较少，用户比较固定的系统实用\n",
    "user_similarity_matrix = np.matrix(np.zeros(shape = (n_users,n_users)),float)\n",
    "\n",
    "for ui in range(n_users):\n",
    "    user_similarity_matrix[ui,ui] = 1.0\n",
    "    if ui % 100 == 0:\n",
    "        print('ui = {}'.format(ui))\n",
    "    for uj in range(ui+1,n_users):\n",
    "        user_similarity_matrix[uj,ui]=user_similarity(ui,uj)\n",
    "        user_similarity_matrix[ui,uj] = user_similarity_matrix[uj,ui]\n",
    "cPickle.dump(user_similarity_matrix,open('ml-100k/user_similarity_matrix.pkl','wb'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 预测用户对item的打分\n",
    "def User_CF_pred(uid,iid):\n",
    "    sim_accumnlate = 0.0\n",
    "    rat_acc = 0.0\n",
    "    for user_id in item_users[iid]: # 哪些用户对这个物品打过分\n",
    "        # 计算当前用户与打过分的用户相似度\n",
    "        sim = user_similarity_matrix[uid,user_id]\n",
    "        if sim != 0:\n",
    "            rat_acc += sim * (user_item_scores[user_id,iid] - user_ms[user_id])\n",
    "            sim_accumnlate += np.abs(sim)\n",
    "    # 估计最后结果，如果为0就给均分\n",
    "    if sim_accumnlate != 0:\n",
    "        score = user_ms[uid] + rat_acc/sim_accumnlate\n",
    "    else:\n",
    "        score = user_ms[uid]\n",
    "    return score"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 对物品进行推荐\n",
    "\n",
    "def recommend(user):\n",
    "    cur_user_id = users_index[user]\n",
    "    \n",
    "    # 训练集中用户打过分的item\n",
    "    cur_user_items = user_items[cur_user_id]\n",
    "    \n",
    "    # 该用户对所有物品打分\n",
    "    user_item_scores = np.zeros(n_items)\n",
    "    \n",
    "    # 预测打分\n",
    "    for i in range(n_items):\n",
    "        if i not in cur_user_items:\n",
    "            user_item_scores[i] = User_CF_pred(cur_user_id,i)\n",
    "    # 用元组来存（分数，物品id）\n",
    "    sort_index = sorted(((e,i) for i,e in enumerate(list(user_item_scores))),reverse=True)\n",
    "    columns = ['item_id','score']\n",
    "    df = pd.DataFrame(columns=columns)\n",
    "    \n",
    "    for i in range(0,len(sort_index)):\n",
    "        cur_item_index = sort_index[i][1]\n",
    "        # 把index转化乘list然后通过index定位value所在位置，然后再将key（物品真正的id）转化成list，找到真正的item id\n",
    "        cur_item = list(items_index.keys())[list(items_index.values()).index(cur_item_index)]\n",
    "        \n",
    "        if ~np.isnan(sort_index[i][0]) and cur_item_index not in cur_user_items:\n",
    "            df.loc[len(df)] = [cur_item,sort_index[i][0]]\n",
    "    return df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3, 4, 5, 7, 8, 9, 11, 13, 15, 16, 18, 19, 21, 22, 25, 26, 28, 29, 30, 32, 34, 35, 37, 38, 40, 41, 42, 43, 45, 46, 48, 50, 52, 55, 57, 58, 59, 63, 66, 68, 71, 75, 77, 79, 83, 87, 88, 89, 93, 94, 95, 99, 101, 105, 106, 109, 110, 111, 115, 116, 119, 122, 123, 124, 126, 127, 131, 133, 135, 136, 137, 138, 139, 141, 142, 144, 146, 147, 149, 152, 153, 156, 158, 162, 165, 166, 167, 168, 169, 172, 173, 176, 178, 179, 181, 182, 187, 191, 192, 194, 195, 197, 198, 199, 203, 204, 205, 207, 211, 216, 217, 220, 223, 231, 234, 237, 238, 239, 240, 244, 245, 246, 247, 249, 251, 256, 257, 261, 263, 268, 269, 270, 271, 10, 14, 100, 242, 255, 258, 272, 273, 274, 275, 276, 277, 278, 282, 283, 284, 285, 286, 287, 288, 289, 291, 293, 294, 295, 296, 300, 302, 304, 305, 306, 309, 310, 311, 260, 303, 317, 319, 320, 321, 322, 325, 326, 329, 333, 336, 338, 339, 340, 342, 344, 346, 347, 352, 353, 355, 210, 301, 324, 327, 328, 358, 359, 360, 362, 70, 121, 145, 151, 163, 174, 183, 186, 189, 200, 208, 226, 228, 229, 233, 235, 250, 365, 366, 367, 368, 371, 373, 374, 375, 378, 380, 381, 383, 386, 387, 390, 392, 395, 396, 398, 399, 401, 404, 405, 406, 408, 409, 412, 414, 415, 416, 419, 420, 425, 427, 430, 431, 432, 434, 437, 438, 440, 442, 443, 446, 447, 448, 449, 450, 451, 452, 455, 456, 12, 47, 64, 132, 170, 177, 185, 202, 259, 298, 308, 410, 435, 460, 461, 462, 464, 465, 468, 472, 473, 474, 482, 485, 489, 490, 491, 493, 494, 495, 496, 497, 501, 502, 503, 504, 505, 507, 510, 512, 514, 516, 519, 520, 522, 527, 529, 530, 531, 533, 535, 536, 537, 538, 539, 23, 27, 31, 39, 51, 56, 62, 69, 73, 80, 86, 91, 96, 97, 98, 125, 134, 140, 143, 157, 164, 175, 180, 201, 213, 214, 215, 219, 230, 265, 318, 341, 356, 357, 379, 384, 391, 402, 403, 417, 421, 429, 433, 436, 441, 444, 463, 470, 487, 488, 499, 506, 509, 513, 521, 523, 540, 544, 545, 546, 547, 549, 551, 552, 556, 557, 558, 561, 565, 566, 568, 569, 571, 572, 573, 574, 575, 577, 578, 583, 584, 585, 586, 588, 590, 591, 592, 593, 594, 595, 596, 597, 598, 600, 601, 602, 603, 605, 606, 607, 609, 610, 611, 612, 613, 615, 616, 617, 620, 625, 628, 629, 631, 632, 634, 635, 638, 639, 640, 641, 646, 649, 651, 652, 655, 656, 660, 663, 664, 665, 666, 668, 670, 671, 672, 677, 678, 679, 682, 82, 222, 227, 241, 243, 518, 684, 688, 385, 483, 690, 33, 60, 85, 129, 161, 221, 334, 467, 478, 479, 480, 484, 511, 525, 582, 589, 604, 654, 686, 692, 693, 694, 697, 699, 700, 701, 707, 708, 709, 24, 54, 107, 196, 290, 332, 370, 372, 382, 393, 423, 508, 526, 580, 659, 662, 715, 716, 717, 718, 719, 724, 727, 728, 729, 731, 737, 738, 742, 745, 747, 748, 749, 750, 159, 17, 49, 53, 67, 78, 92, 117, 128, 155, 160, 184, 188, 193, 209, 218, 224, 225, 262, 264, 279, 280, 292, 299, 307, 313, 316, 323, 348, 350, 394, 400, 411, 413, 424, 439, 445, 453, 471, 475, 492, 515, 541, 550, 553, 554, 559, 563, 576, 619, 621, 630, 636, 637, 647, 650, 657, 661, 667, 669, 674, 675, 687, 689, 691, 706, 712, 722, 732, 733, 736, 739, 740, 746, 751, 754, 756, 757, 758, 760, 761, 762, 763, 765, 767, 768, 769, 770, 771, 773, 774, 775, 777, 778, 779, 785, 789, 790, 791, 792, 793, 795, 796, 797, 798, 799, 800, 801, 803, 804, 806, 810, 811, 813, 815, 817, 818, 819, 822, 824, 825, 826, 827, 829, 832, 833, 834, 835, 837, 838, 841, 843, 845, 846, 849, 850, 851, 853, 855, 862, 863, 865, 867, 870, 871, 872, 873, 879, 880, 881, 882, 883, 884, 886, 889, 891, 893, 895, 896, 897, 899, 900, 902, 903, 904, 905, 909, 911, 914, 916, 917, 81, 428, 524, 820, 919, 920, 921, 923, 118, 248, 252, 297, 676, 823, 866, 925, 926, 927, 928, 930, 931, 934, 935, 936, 938, 469, 476, 498, 642, 939, 941, 943, 945, 946, 947, 948, 150, 6, 61, 65, 72, 212, 236, 418, 486, 517, 528, 614, 627, 633, 702, 704, 714, 735, 753, 781, 805, 856, 949, 951, 952, 956, 957, 958, 959, 962, 963, 967, 969, 972, 973, 887, 148, 281, 330, 564, 567, 680, 854, 858, 874, 876, 877, 878, 975, 976, 978, 980, 981, 983, 984, 986, 987, 988, 989, 990, 991, 992, 993, 994, 840, 932, 996, 998, 1000, 1002, 1003, 102, 154, 171, 713, 780, 961, 1004, 1007, 929, 968, 369, 458, 683, 831, 1010, 1011, 1012, 1013, 1014, 1017, 859, 312, 1019, 315, 892, 875, 1020, 1021, 1022, 1023, 898, 1024, 266, 937, 1026, 1027, 84, 112, 673, 681, 940, 1016, 1028, 1029, 1031, 1032, 1033, 1035, 1036, 1037, 337, 343, 1038, 1039, 103, 658, 720, 755, 924, 1042, 1046, 1047, 1048, 1050, 254, 354, 542, 581, 1052, 1054, 1055, 1056, 90, 190, 764, 1001, 1059, 1063, 1064, 1065, 477, 698, 725, 997, 1018, 1066, 1067, 1069, 1071, 1072, 1073, 1074, 1075, 1077, 1079, 1080, 1081, 1083, 1008, 705, 741, 864, 1085, 1087, 114, 232, 376, 794, 1057, 1091, 710, 1096, 645, 730, 1006, 1070, 1084, 1089, 1101, 1105, 44, 466, 608, 618, 622, 644, 953, 1005, 1093, 1111, 1112, 1114, 1115, 1119, 1120, 1060, 1121, 1122, 1125, 76, 723, 744, 1009, 1129, 1130, 1132, 1133, 1134, 1135, 108, 828, 1138, 389, 847, 1140, 1041, 1044, 1142, 1144, 1030, 1145, 1146, 685, 844, 1051, 1147, 1148, 894, 331, 351, 1152, 955, 1153, 1154, 1155, 1157, 1159, 906, 726, 1078, 1128, 1163, 944, 1049, 543, 345, 481, 782, 1103, 1113, 1136, 1166, 1170, 1171, 1172, 1173, 1174, 888, 1176, 570, 648, 783, 802, 808, 1118, 1177, 1178, 1179, 1180, 1181, 1185, 1186, 1188, 1191, 20, 454, 500, 703, 721, 836, 942, 954, 965, 1045, 1097, 1137, 1192, 1195, 1196, 1199, 1201, 1202, 1203, 1204, 1205, 1126, 120, 363, 364, 743, 922, 974, 1040, 1090, 1194, 1208, 1210, 1211, 1214, 1216, 587, 786, 809, 1058, 1110, 1209, 1218, 1219, 1221, 1222, 1225, 532, 623, 960, 971, 1116, 1227, 1228, 1231, 349, 885, 908, 1233, 1234, 1235, 1237, 979, 548, 1076, 1240, 130, 407, 534, 1242, 1243, 1139, 1161, 1245, 397, 1182, 1229, 1247, 1249, 1106, 1251, 696, 772, 253, 1220, 1226, 1244, 1254, 1255, 1256, 1258, 977, 1165, 459, 1034, 1260, 1264, 1265, 1266, 1168, 1267, 1269, 388, 1246, 1270, 1271, 901, 869, 1053, 426, 555, 752, 816, 982, 1088, 1095, 1151, 1275, 1276, 1278, 1279, 1280, 1217, 1117, 335, 985, 1282, 1283, 1197, 821, 890, 1025, 1215, 1273, 1287, 1288, 1289, 1290, 1291, 1293, 1295, 1296, 966, 1098, 1286, 1299, 1300, 1301, 562, 1303, 918, 1092, 933, 1200, 1304, 1308, 995, 1086, 1262, 1311, 1313, 1314, 1315, 1127, 104, 1061, 1102, 1150, 1162, 1187, 1252, 1277, 1281, 1302, 1317, 1319, 1321, 1323, 1325, 1326, 1328, 1329, 1331, 1335, 1337, 1338, 1339, 1340, 1341, 1345, 1349, 1350, 1352, 1353, 1355, 1356, 1358, 1359, 1363, 1365, 1366, 1368, 1372, 1380, 1381, 1382, 1389, 1391, 1394, 1160, 1167, 1297, 1213, 1336, 1385, 1399, 1263, 1400, 1401, 1402, 1404, 1407, 624, 1107, 1207, 1408, 1410, 1411, 1412, 1193, 1413, 1414, 1415, 1416, 1241, 1420, 1354, 812, 1419, 950, 1131, 1224, 1268, 1398, 1421, 1422, 1423, 1426, 1428, 361, 1062, 1175, 1395, 1429, 1431, 1432, 1433, 1434, 1272, 1333, 1435, 457, 1330, 695, 1437, 377, 422, 734, 1206, 1250, 1439, 1440, 1284, 1068, 1143, 1149, 653, 842, 964, 970, 1100, 1169, 1285, 1369, 1445, 1446, 1447, 1448, 1449, 1450, 1452, 1453, 1460, 1461, 1451, 1332, 1465, 1109, 1467, 1468, 1183, 1443, 1469, 1470, 36, 1424, 1471, 113, 1472, 1474, 1475, 206, 579, 860, 1476, 1361, 1397, 1427, 1444, 1478, 1479, 759, 807, 915, 1141, 1232, 1481, 1482, 1483, 1274, 1305, 1480, 1485, 1486, 1488, 1490, 1491, 1494, 1496, 1499, 1099, 1466, 1473, 1239, 1375, 1502, 1248, 1489, 1298, 1223, 1379, 1506, 1507, 1094, 1230, 1509, 1510, 1511, 1015, 1512, 1513, 1514, 1456, 1515, 1386, 1516, 787, 1253, 1519, 1522, 1487, 1523, 912, 1518, 74, 1198, 1312, 1504, 1525, 1124, 1526, 1528, 1082, 1392, 1294, 1396, 1524, 1212, 1530, 1322, 1425, 1438, 1531, 1532, 1158, 1462, 1495, 1535, 1537, 560, 999, 1409, 1539, 1371, 1484, 1184, 1393, 1540, 1541, 1542, 1238, 626, 643, 776, 784, 788, 861, 1043, 1104, 1108, 1261, 1306, 1307, 1334, 1346, 1384, 1387, 1405, 1441, 1442, 1464, 1517, 1529, 1544, 1545, 1546, 1547, 1548, 1549, 1551, 1552, 1553, 1556, 1558, 1559, 1560, 1564, 1566, 1567, 1568, 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1579, 1580, 1581, 1583, 1584, 1585, 1587, 1588, 1589, 1590, 1591, 1592, 1360, 1593, 1189, 1521, 1594, 1550, 1347, 267, 1595, 1596, 1597, 848, 1418, 1598, 1599, 1600, 1378, 1534, 1601, 1602, 1318, 1367, 1603, 1383, 1403, 1190, 1454, 1324, 1604, 1164, 1377, 1605, 1606, 868, 1607, 1123, 1608, 1388, 766, 1609, 1610, 1611, 1612, 1613, 1459, 1614, 1555, 1615, 1616, 1316, 1508, 1527, 1430, 1617, 1618, 1327, 314, 1619, 1620, 1376, 1621, 1362, 1622, 910, 1344, 1501, 1623, 1624, 1625, 1259, 1357, 1503, 1406, 1374, 1626, 1627, 913, 1257, 1351, 1370, 1436, 1538, 1554, 1578, 1628, 1629, 1630, 1631, 1632, 1633, 1634, 1635, 1636, 1637, 1638, 1639, 1640, 1641, 1642, 1643, 1644, 1645, 1646, 1647, 1648, 1649, 1650, 1651, 1342, 1652, 1653, 1654, 1655, 1455, 1656, 1657, 1658, 1497, 1659, 1660, 1661, 1463, 1292, 1662, 1500, 1390, 1417, 1477, 1663, 1664, 1665, 1666, 1667, 1668, 1669, 1670, 1671, 839, 1672, 1673, 1674, 1675, 1676, 1677, 1678, 1679, 1680, 907, 1681, 1682]\n"
     ]
    }
   ],
   "source": [
    "print(list(items_index.keys()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>user_id</th>\n",
       "      <th>item_id</th>\n",
       "      <th>rating</th>\n",
       "      <th>timestamp</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>6</td>\n",
       "      <td>5</td>\n",
       "      <td>887431973</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>10</td>\n",
       "      <td>3</td>\n",
       "      <td>875693118</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>2</td>\n",
       "      <td>1</td>\n",
       "      <td>12</td>\n",
       "      <td>5</td>\n",
       "      <td>878542960</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>3</td>\n",
       "      <td>1</td>\n",
       "      <td>14</td>\n",
       "      <td>5</td>\n",
       "      <td>874965706</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>4</td>\n",
       "      <td>1</td>\n",
       "      <td>17</td>\n",
       "      <td>3</td>\n",
       "      <td>875073198</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   user_id  item_id  rating  timestamp\n",
       "0        1        6       5  887431973\n",
       "1        1       10       3  875693118\n",
       "2        1       12       5  878542960\n",
       "3        1       14       5  874965706\n",
       "4        1       17       3  875073198"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 测试\n",
    "triplet_cols = ['user_id','item_id','rating','timestamp']\n",
    "df_triplet_test = pd.read_csv('ml-100k/u1.test',sep='\\t',encoding='Latin-1',names=triplet_cols)\n",
    "df_triplet_test.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "599 is new item. \n",
      "711 is new item. \n",
      "814 is new item. \n",
      "830 is new item. \n",
      "852 is new item. \n",
      "857 is new item. \n",
      "1156 is new item. \n",
      "1236 is new item. \n",
      "1309 is new item. \n",
      "1310 is new item. \n",
      "1320 is new item. \n",
      "1343 is new item. \n",
      "1348 is new item. \n",
      "1364 is new item. \n",
      "1373 is new item. \n",
      "1457 is new item. \n",
      "1458 is new item. \n",
      "1492 is new item. \n",
      "1493 is new item. \n",
      "1498 is new item. \n",
      "1505 is new item. \n",
      "1520 is new item. \n",
      "1533 is new item. \n",
      "1536 is new item. \n",
      "1543 is new item. \n",
      "1557 is new item. \n",
      "1561 is new item. \n",
      "1562 is new item. \n",
      "1563 is new item. \n",
      "1565 is new item. \n",
      "1582 is new item. \n",
      "1586 is new item. \n"
     ]
    }
   ],
   "source": [
    "# 统计总的用户\n",
    "unique_users_test = df_triplet_test['user_id'].unique()\n",
    "\n",
    "# 为每个用户推荐20个商品\n",
    "n_rec_items = 20\n",
    "\n",
    "#性能评价计算精确率和召回率\n",
    "n_hits = 0\n",
    "n_total_rec_items = 0\n",
    "n_test_items = 0\n",
    "\n",
    "#所有被推荐商品的集合，用于计算覆盖度\n",
    "all_rec_items = set()\n",
    "\n",
    "# 残差平方和，用于计算RMSE\n",
    "rss_test = 0.0\n",
    "\n",
    "# 对每个测试用户\n",
    "for user in unique_users_test:\n",
    "    if user not in users_index:\n",
    "        print('{} is new user'.format(user))\n",
    "        continue\n",
    "    user_records_test = df_triplet_test[df_triplet_test.user_id == user]\n",
    "    \n",
    "    rec_items = recommend(user)\n",
    "    \n",
    "    for i in range(n_rec_items):\n",
    "        item = rec_items.iloc[i]['item_id']\n",
    "        \n",
    "        if item in user_records_test['item_id'].values:\n",
    "            n_hits +=1\n",
    "            \n",
    "        all_rec_items.add(item)\n",
    "    \n",
    "    # 计算rmse\n",
    "    for i in range(user_records_test.shape[0]):\n",
    "        item = user_records_test.iloc[i]['item_id']\n",
    "        score = user_records_test.iloc[i]['rating']\n",
    "        \n",
    "        df1 = rec_items[rec_items.item_id == item]\n",
    "        if df1.shape[0] == 0:\n",
    "            print('{} is new item. '.format(item))\n",
    "            continue\n",
    "        pre_score = df1['score'].values[0]\n",
    "        rss_test += (pre_score - score)**2\n",
    "    # 推荐item 总数\n",
    "    n_total_rec_items += n_rec_items\n",
    "    n_test_items += user_records_test.shape[0]\n",
    "\n",
    "precision = n_hits / (1.0 * n_total_rec_items)\n",
    "recall = n_hits / (1.0 * n_test_items)\n",
    "\n",
    "# 覆盖率\n",
    "coverage = len(all_rec_items) / (1.0 * n_items)\n",
    "\n",
    "# 打分均方误差\n",
    "rmse = np.sqrt(rss_test/df_triplet_test.shape[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.001851851851851852"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "precision"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.00085"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "recall"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.17454545454545456"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "coverage"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.9658669077094278"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "rmse"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
